home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-05-11 | 8.6 KB | 276 lines | [TEXT/MPS ] |
- { Miscellaneous routines used with the serial port XCMDs.
-
- This file is included in the other serial port XCMD source code files -- i.e., it is not compiled and
- linked separately.
-
- Copyright © 1987,88 Apple Computer, Inc.
-
- Initial coding 9/87 by Harry Chesley.
- }
-
- const
-
- GLOBALNAME = 'SPortGlobals'; { Where to store a handle to our globals. }
- MAXPARMBLKS = 4; { Maximum number of outgoing parameter blocks per port. }
- WRAPCOLUMN = 75; { The column at which to autowrap, if we are autowrapping. }
- MAXWRAP = 30; { The maximum number of characters to carry forward when autowrapping. }
-
- type
-
- { Serial port global data: }
- PortDataType =
- record
- isOpen: boolean; { Whether the port is open yet. }
- portInDev, portOutDev: integer; { The device numbers for input and output. }
- byteConfig: integer; { The byte format being used. }
- shakes: SerShk; { Flow control, etc. }
- sendLFs: boolean; { Whether to send linefeeds after carriage returns. }
- doEcho: boolean; { Whether to echo input. }
- doEdit: boolean; { Whether to allow backspace editing on input. }
- stripIncoming: boolean; { Whether to strip the top bit on incoming bytes. }
- stripControls: boolean; { Whether to strip out non-return, non-tab control characters. }
- autoWrap: boolean; { Auto-wrap input and output. }
- currentColumn: integer; { Column we're about to write into. }
- inputBuffer: Ptr; { The input buffer, or nil if there is none allocated. }
- parmBlks: array [1..MAXPARMBLKS] of ParmBlkPtr; { Asynch IO blocks (for output). }
- end;
-
- OurGlobalHandle = ^OurGlobalPtr;
- OurGlobalPtr = ^OurGlobalType;
-
- { Global data: }
- OurGlobalType =
- record
- selectedPort: SPortSel; { Which port is selected. }
- ports: array [SPortSel] of PortDataType; { The port data for each port. }
- end;
-
- var
-
- { Global data. (Note: Although this is called global, it's actually allocated as local to the top-level
- XCMD routine, and therefore only stays around for the duration of the XCMD execution. Being
- able to think of it as globals, and not having to pass it to each subroutine called, however, is
- extremely useful. This is perhaps the best reason for using Pascal rather than C for writing
- XCMDs.) }
-
- Globals: OurGlobalHandle; { Global data (this handle is saved in a HyperTalk global between XCMDs). }
- ThisSPort: PortDataType; { A temporary copy of the global data for the currently selected port. }
-
- procedure GetStrGlobal(name: str255; var glob: str255);
- { Set glob to the global string specified by name. }
-
- var globHand: Handle;
-
- begin
- { Get the HyperTalk global. }
- globHand := GetGlobal(name);
- { Convert it to a Pascal string. }
- if globHand = nil then glob := ''
- else
- begin
- ZeroToPas(globHand^,glob);
- DisposHandle(globHand);
- end;
- end;
-
- function GetLongGlobal(name: str255): longInt;
- { Return the global string specified by name, interpreted as a long integer. }
-
- var globStr: str255;
-
- begin
- { Get the HyperTalk global into a Pascal string. }
- GetStrGlobal(name,globStr);
- { Convert it to a longInt. }
- GetLongGlobal := StrToLong(globStr);
- end;
-
- procedure SetStrGlobal(name: str255; glob: str255);
- { Set the global string specified by name to glob. }
-
- var globHand: Handle;
-
- begin
- { Convert the string to a HyperTalk style handle-string. }
- globHand := PasToZero(glob);
- { Set the global. }
- SetGlobal(name,globHand);
- { Dispose of our copy. }
- DisposHandle(globHand);
- end;
-
- procedure SetLongGlobal(name: str255; globLong: longInt);
- {Set the global string specified by name to a string that represents the number in globLong. }
-
- var globStr: str31;
-
- begin
- { Convert the longInt to a Pascal string. }
- globStr := LongToStr(globLong);
- { Set the HyperTalk global to that. }
- SetStrGlobal(name,globStr);
- end;
-
- procedure SetUpSPortGlobals;
- { Create the handle we use for our global variables, and remember it in a HyperCard global. }
-
- var portIndex: SPortSel;
-
- begin
- { Get the value of the global that holds our globals handle. }
- Globals := OurGlobalHandle(GetLongGlobal(GLOBALNAME));
- { If it's empty (which will evaluate to zero or nil), then we need to create it. }
- if Globals = nil then
- begin
- { Make the handle. }
- Globals := OurGlobalHandle(NewHandle(sizeof(OurGlobalType)));
- if Globals = nil then Fail('could not allocate global variable space');
- { Default to modem port. }
- Globals^^.selectedPort := sPortA;
- { Mark both ports as closed. }
- for portIndex := sPortA to sPortB do
- with Globals^^.ports[portIndex] do
- begin
- isOpen := false;
- { Set the device numbers. }
- case portIndex of
- sPortA:
- begin portInDev := -6; portOutDev := -7; end;
- sPortB:
- begin portInDev := -8; portOutDev := -9; end;
- end;
- { Set the default byte format. }
- byteConfig := baud1200+stop10+noParity+data8;
- { Set the rest of the port defaults. }
- with shakes do
- begin
- fXOn := 0; fCTS := 0; errs := 0; evts := 0; fInX := 0;
- end;
- { Set no auto-linefeed, no echo, no edit, no strip top bits. }
- sendLFs := false;
- doEcho := false;
- doEdit := false;
- stripIncoming := true;
- stripControls := false;
- autoWrap := false;
- currentColumn := 1;
- { No input buffer (i.e., use the default port buffer). }
- inputBuffer := nil;
- end;
- { Store our globals handle in the HyperTalk global, so we can get it later. }
- SetLongGlobal(GLOBALNAME,ord4(Globals));
- end;
- { Copy the globals for the active port into a more easily accessed location. }
- ThisSPort := Globals^^.ports[Globals^^.selectedPort];
- end;
-
- procedure EnsureOpenPort;
- { Check if the port is open. If it isn't, then open it now. }
-
- var i: integer;
- actualIn, actualOut: integer;
- whichPort: SPortSel;
-
- begin
- if not ThisSPort.isOpen then
- begin
- { Get the port. }
- whichPort := Globals^^.selectedPort;
-
- { Allocate and init a gaggle of parmBlks for use with asynchronous output. }
- for i := 1 to MAXPARMBLKS do
- begin
- Globals^^.ports[whichPort].parmBlks[i] := ParmBlkPtr(NewPtr(sizeof(ParamBlockRec)));
- with Globals^^.ports[whichPort].parmBlks[i]^ do
- begin
- ioCompletion := nil;
- ioResult := noErr;
- ioRefNum := Globals^^.ports[whichPort].portOutDev;
- ioBuffer := nil;
- end;
- end;
- { Copy the new info into the current port. }
- ThisSPort := Globals^^.ports[whichPort];
-
- { Actually open the port. }
- if Globals^^.selectedPort = sPortA then
- begin
- if (OpenDriver('.AOut',actualOut) <> noErr) or (OpenDriver('.AIn',actualIn) <> noErr) then
- Fail('OpenDriver failed');
- end
- else
- begin
- if (OpenDriver('.BOut',actualOut) <> noErr) or (OpenDriver('.BIn',actualIn) <> noErr) then
- Fail('OpenDriver failed');
- end;
- with ThisSPort do
- begin
- { Be paranoid about making sure everything is opened properly. }
- if (actualOut <> portOutDev) or (actualIn <> portInDev) then Fail('openDriver failed');
- { Set the defaults. }
- if SerHShake(portInDev,shakes) <> noErr then Fail('SerHShake failed');
- if SerHShake(portOutDev,shakes) <> noErr then Fail('SerHShake failed');
- if (SerReset(portInDev,byteConfig) <> noErr) or (SerReset(portOutDev,byteConfig) <> noErr) then
- Fail('SerReset failed');
- Globals^^.ports[Globals^^.selectedPort].isOpen := true;
- isOpen := true;
- end;
- end;
- end;
-
- procedure GetStrParm(n: integer; var str: str255);
- { Get the nth parameter into str. }
-
- begin
- ZeroToPas(paramPtr^.params[n]^,str);
- end;
-
- function GetLongParm(n: integer): longInt;
- { Return the nth parameter string, interpreted as a long integer. }
-
- var str: str255;
-
- begin
- ZeroToPas(paramPtr^.params[n]^,str);
- GetLongParm := StrToNum(str);
- end;
-
- function FindFreeIOBlk: ParmBlkPtr;
- { Find and return a free I/O parameter block. If none are free, return nil. }
-
- var i: integer;
-
- begin
- for i := 1 to MAXPARMBLKS do
- if ThisSPort.parmBlks[i]^.ioResult <= noErr then
- begin
- FindFreeIOBlk := ThisSPort.parmBlks[i];
- exit(FindFreeIOBlk);
- end;
- FindFreeIOBlk := nil;
- end;
-
- function WaitForFreeIOBlk: ParmBlkPtr;
- { Wait for an I/O block to be free and return it. }
-
- var theBlock: ParmBlkPtr;
-
- begin
- repeat theBlock := FindFreeIOBlk until theBlock <> nil;
- WaitForFreeIOBlk := theBlock;
- end;
-
- procedure WaitForAllFree;
- { Wait for all I/O blocks to free up (i.e., wait for all output to finish). }
-
- var allDone: boolean;
- i: integer;
-
- begin
- repeat
- allDone := true;
- for i := 1 to MAXPARMBLKS do
- if ThisSPort.parmBlks[i]^.ioResult > noErr then allDone := false;
- until allDone;
- end;
-